import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;

import choco.Choco;
import choco.cp.model.CPModel;
import choco.cp.solver.CPSolver;
import choco.kernel.model.variables.integer.IntegerVariable;
import choco.kernel.model.variables.scheduling.TaskVariable;


public class Exercice3_OLD extends CPModel {
	
	private static final String FONDATIONS = "fondations";
	private static final String MURS = "murs";
	private static final String TOITS_PLAFONDS = "toits et plafonds";
	private static final String PLOMBERIE_ELECTRICITE = "plomberie et electricite";
	private static final String SOL = "sol";
	private static final String PEINTURE = "peinture";
	private static final String FACADE = "facade";
	private static final String NETTOYAGE = "nettoyage";
	private static final String MENUISERIE = "menuiserie";
	private static final String FINITIONS = "finitions";
	
	//Le nombre maximal d'heures du projet, correspondant à la somme des heures des activités à la suite
	static int maxHeures = 0;
	static int maxJours = 0;
	//Le nombre maximal d'ouvriers pouvant intervenir simultanément sur le chantier
	private static final int MAX_OUVRIERS = 5;
	
	private TaskVariable[] planning;
	private IntegerVariable finDuProjet;
	private IntegerVariable[] ouvriersParCreneau; //Pour chaque créneau (chaque heure), le nombre d'ouvriers
	private int nActivities;
	
	HashMap<String, Integer> activitiesTime;//Temps en heures de chaque activité
	HashMap<String, Integer> activitiesID;
	HashMap<String, Integer> activitiesRessources;
	
	public Exercice3_OLD() {
		super();
		activitiesTime = new HashMap<String, Integer>();
		activitiesTime.put(FONDATIONS, 40);
		activitiesTime.put(MURS, 103);
		activitiesTime.put(TOITS_PLAFONDS, 55);
		activitiesTime.put(PLOMBERIE_ELECTRICITE, 48);
		activitiesTime.put(SOL, 63);
		activitiesTime.put(PEINTURE, 16);
		activitiesTime.put(FACADE, 104);
		activitiesTime.put(NETTOYAGE, 7);
		activitiesTime.put(MENUISERIE, 22);
		activitiesTime.put(FINITIONS, 15);		
		
		activitiesID = new HashMap<String, Integer>();
		activitiesID.put(FONDATIONS, 0);
		activitiesID.put(MURS, 1);
		activitiesID.put(TOITS_PLAFONDS, 2);
		activitiesID.put(PLOMBERIE_ELECTRICITE, 3);
		activitiesID.put(SOL, 4);
		activitiesID.put(PEINTURE, 5);
		activitiesID.put(FACADE, 6);
		activitiesID.put(NETTOYAGE, 7);
		activitiesID.put(MENUISERIE, 8);
		activitiesID.put(FINITIONS, 9);	
		
		// Les differentes activites necessitent le nombre d’ouvriers suivants :
		// fondations : 2 personnes, murs : 4 personnes, toit-plafonds : 5 personnes, 
		// plomberie et electricite : 4 personnes, sol : 5 personnes,
		// peinture : 3 personnes, facade : 1 personne, nettoyage : 2 personnes,
		// menuiserie : 2 personnes, finitions : 3 personnes.
		
		activitiesRessources = new HashMap<String, Integer>();
		activitiesRessources.put(FONDATIONS, 2);
		activitiesRessources.put(MURS, 4);
		activitiesRessources.put(TOITS_PLAFONDS, 5);
		activitiesRessources.put(PLOMBERIE_ELECTRICITE, 4);
		activitiesRessources.put(SOL, 5);
		activitiesRessources.put(PEINTURE, 3);
		activitiesRessources.put(FACADE, 1);
		activitiesRessources.put(NETTOYAGE, 2);
		activitiesRessources.put(MENUISERIE, 2);
		activitiesRessources.put(FINITIONS, 3);		
		
		nActivities = activitiesID.size();
		
		for (String activity : activitiesTime.keySet()) {
			maxHeures += activitiesTime.get(activity);
		}
		
		planning = new TaskVariable[activitiesTime.size()];		
		for (String activity : activitiesTime.keySet()) {
			planning[activitiesID.get(activity)] = Choco.makeTaskVar(activity, 0, maxHeures, activitiesTime.get(activity));
			addVariable(planning[activitiesID.get(activity)]);
		}
		finDuProjet = planning[activitiesID.get(FINITIONS)].end();
		
		ouvriersParCreneau = new IntegerVariable[maxHeures];
		for (int i = 0; i < maxHeures; i++) {
			ouvriersParCreneau[i] = Choco.makeIntVar("nb ouvriers heure "+i, 0, MAX_OUVRIERS);
			addVariable(ouvriersParCreneau[i]);
		}		
		this.postConstraints();
	}
	
	public void postConstraints() {
		
		// Apres la construction des fondations, les ouvriers peuvent entreprendre le dressage 
		// des murs.
		addPrecedence(FONDATIONS, MURS);

		// Une fois cette tache terminee, nous pouvons commencer le toit, la plomberie et le sol.
		addPrecedence(MURS, TOITS_PLAFONDS);		
		addPrecedence(MURS, PLOMBERIE_ELECTRICITE);
		addPrecedence(MURS, SOL);
		
		//  Les activites de peinture ne peuvent pas etre lancees avant le sechage des plafonds.
		addPrecedence(TOITS_PLAFONDS, PEINTURE);
		
		// la facade et le nettoyage du jardin ne peuvent etre accomplies avant d’avoir termine 
		// la plomberie. 
		addPrecedence(PLOMBERIE_ELECTRICITE, FACADE);
		addPrecedence(PLOMBERIE_ELECTRICITE, NETTOYAGE);
		//  La confection du toit devra  egalement preceder le nettoyage
		addPrecedence(TOITS_PLAFONDS, NETTOYAGE);		
		// La menuiserie survient apres la peinture, la facade et le sol	
		addPrecedence(PEINTURE, MENUISERIE);	
		addPrecedence(FACADE, MENUISERIE);		
		addPrecedence(SOL, MENUISERIE);	
		// Enfin, les activites de finition interviennent une fois le chantier termine
		for (String a: activitiesTime.keySet()) {
			if(a!=FINITIONS) addPrecedence(a, FINITIONS);
		}		
		
		// Au plus 5 ouvriers peuvent intervenir simultanement sur le chantier.
		// Il ne s’agira pas forcement des memes personnes (selon les competences).
		int[] ressourcesParActivite = new int[nActivities];

		for (String activity : activitiesID.keySet()) {
			ressourcesParActivite[activitiesID.get(activity)] = activitiesRessources.get(activity);
		}
		
		IntegerVariable[] activiteALieuMomentI;
		
		for (int i = 0; i < maxHeures; i++) {
			//Pour chaque activité, on regarde si elle a lieu à l'heure i
			activiteALieuMomentI = Choco.makeIntVarArray("Act", nActivities, 0, 1);
			for (int ID : activitiesID.values()) {
				
				IntegerVariable beginVar=Choco.makeIntVar("Debut activite ID "+ID+" heure "+i,0,1);
				IntegerVariable endVar=Choco.makeIntVar("Fin activite ID "+ID+" heure "+i,0,1);
				addConstraint( Choco.reifiedConstraint(beginVar, Choco.leq(planning[ID].start(), i)));
				addConstraint( Choco.reifiedConstraint(endVar, Choco.gt(planning[ID].end(), i)));
				addConstraint( Choco.reifiedAnd(activiteALieuMomentI[ID], beginVar,endVar));				
				
			}
			//Le nombre d'ouvriers par jour est égal à la somme des ouvriers utilisés
			// par les activités 0ayant lieu ce jour-là
			addConstraint( Choco.eq(ouvriersParCreneau[i], 
									Choco.scalar(ressourcesParActivite, activiteALieuMomentI)) );


		}

	}
	
	/**
	 * Méthode permettant d'ajouter la contrainte "la tâche task1 a lieu avant la tâche task2"
	 * @param task1
	 * @param task2
	 */
	public void addPrecedence(String task1, String task2) {
		addConstraint(
				Choco.leq(
						planning[activitiesID.get(task1)].end(), 
						planning[activitiesID.get(task2)].start())
						);			
	}

	public CPSolver solve() {
		CPSolver solver = new CPSolver();
		solver.read(this);
		//ChocoLogging.setVerbosity(Verbosity.SOLUTION);
		solver.minimize(solver.getVar(finDuProjet), false);
		return solver;
	}
	
	public void print(CPSolver solver) {
		int heureDeFin = solver.getVar(finDuProjet).getVal();
		System.out.println("Planification des activités intervenant dans la construction d'une villa");
		Calendar debut = new GregorianCalendar(2012, Calendar.MARCH, 1);
		Calendar fin = new GregorianCalendar(2012, Calendar.MARCH, 1);
		fin.add(Calendar.DAY_OF_MONTH, (int)Math.ceil((double)heureDeFin/8.0));
		System.out.println("Début du projet : "+debut.getTime());
		System.out.println("Fin du projet au plut tôt : "+fin.getTime());
		System.out.println("Nombre d'heures nécessaires : "+heureDeFin+"\n");
		System.out.println("Nombre de jours nécessaires : entre "+(double)heureDeFin/8.0+" et "+(double)heureDeFin/6.0);

		for (int i = 0; i < planning.length; i++) {
			System.out.println(solver.getVar(planning[i]));
		}
		System.out.println();
		for (int i = 0; i < maxHeures; i++) {
			System.out.println("Nombre d'ouvriers heure "+i+" : "+solver.getVar(ouvriersParCreneau[i]).getVal());
		}
	}

	public static void main(String[] args) {

		Exercice3_OLD planif = new Exercice3_OLD();
		CPSolver solver = planif.solve();
		planif.print(solver);
	}

}
